/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _H_LIBZLANG_DATETIME_
#define _H_LIBZLANG_DATETIME_

#include "zlang.h"

#include "list.h"
#include "rbtree.h"
#include "rbtree_tpl.h"

#include <sys/types.h>

#if ( defined __unix ) || ( defined __linux__ )
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/socket.h>
#elif ( defined _WIN32 )
#include <winsock2.h>
#include <mswsock.h>
#include <windows.h>
#include <io.h>
#endif

#include <signal.h>

#ifndef OPEN
#if ( defined __linux ) || ( defined __unix )
#define OPEN	open
#elif ( defined _WIN32 )
#define OPEN	_open
#endif
#endif

#ifndef CLOSE
#if ( defined __linux ) || ( defined __unix )
#define CLOSE	close
#elif ( defined _WIN32 )
#define CLOSE	_close
#endif
#endif

#ifndef LSEEK
#if ( defined __linux ) || ( defined __unix )
#define LSEEK	lseek
#elif ( defined _WIN32 )
#define LSEEK	_lseek
#endif
#endif

#ifndef READ
#if ( defined __linux ) || ( defined __unix )
#define READ	read
#elif ( defined _WIN32 )
#define READ	_read
#endif
#endif

#if ( defined __unix ) || ( defined __linux__ )
#define ERRNO           errno
#define SOCKET          int
#define SOCKLEN_T       socklen_t
#define CLOSESOCKET     close
#define INVALID_SOCKET	-1
#elif ( defined _WIN32 )
#define ERRNO           GetLastError()
#ifndef EWOULDBLOCK
#define EWOULDBLOCK     WSAEWOULDBLOCK
#define ECONNABORTED    WSAECONNABORTED
#define EINPROGRESS     WSAEINPROGRESS
#define ECONNRESET      WSAECONNRESET
#define ENOTCONN        WSAENOTCONN
#define EISCONN         WSAEISCONN
#endif
#define SOCKLEN_T       int
#define CLOSESOCKET     closesocket
#endif

#define SET_TCP_REUSEADDR(_sock_) \
	{ \
		int	onoff = 1 ; \
		setsockopt( _sock_ , SOL_SOCKET , SO_REUSEADDR , (void *) & onoff , sizeof(int) ); \
	} \

#if defined(__linux__)
#define SET_TCP_BLOCK(_sock_,_to_block_) \
	{ \
		int	opts; \
		opts = fcntl( _sock_ , F_GETFL ); \
		if( (_to_block_) ) \
			opts &= ~O_NONBLOCK ; \
		else \
			opts |= O_NONBLOCK ; \
		fcntl( _sock_ , F_SETFL , opts ); \
	}
#elif defined(_WIN32)
#define SET_TCP_BLOCK(_sock_,_to_block_) \
	{ \
		unsigned long nonblocking = (_to_block_) ; \
		ioctlsocket( _sock_ , FIONBIO , & nonblocking ); \
	}
#endif

#define SET_TCP_NODELAY(_sock_,_onoff_) \
	{ \
		int	onoff = (int)(_onoff_) ; \
		setsockopt( _sock_ , IPPROTO_TCP , TCP_NODELAY , (void*) & onoff , sizeof(int) ); \
	} \

#define SET_TCP_LINGER(_sock_,_linger_) \
	{ \
		struct linger   lg; \
		if( (_linger_) == -1 ) \
		{ \
			lg.l_onoff = 0 ; \
			lg.l_linger = 0 ; \
		} \
		else \
		{ \
			lg.l_onoff = 1 ; \
			lg.l_linger = (_linger_) ; \
		} \
		setsockopt( _sock_ , SOL_SOCKET , SO_LINGER , (void*) & lg , sizeof(struct linger) ); \
	} \

#define DIFF_TIMEVAL_TO_MILLISECONDS(_begin_timestamp_,_end_timestamp_,_timeout_,_elapse_) \
	{ \
		struct timeval	diff_tv ; \
		int32_t		diff_ms ; \
		diff_tv.tv_sec = (_end_timestamp_).tv_sec - (_begin_timestamp_).tv_sec ; \
		diff_tv.tv_usec = (_end_timestamp_).tv_usec - (_begin_timestamp_).tv_usec ; \
		while( diff_tv.tv_usec < 0 ) \
		{ \
			diff_tv.tv_sec--; \
			diff_tv.tv_usec += 1000000 ; \
		} \
		diff_ms = diff_tv.tv_sec * 1000 + diff_tv.tv_usec ; \
		if( diff_ms > (_timeout_) ) \
			diff_ms = (_timeout_) ; \
		(_timeout_) -= diff_ms ; \
		(_elapse_) += diff_ms ; \
	} \

#ifndef PRIsock
#if defined(__linux__)
#define PRIsock		"d"
#elif defined(_WIN32)
#define PRIsock		"I64u"
#endif
#endif

#ifndef SOCKET_INIT
#if defined(__linux__)
#define SOCKET_INIT	-1
#elif defined(_WIN32)
#define SOCKET_INIT	INVALID_SOCKET
#endif
#endif

struct ZlangTcp
{
	char		ip[ 40 + 1 ] ;
	int32_t		port ;
	int		sock ;
	int32_t		connecting_timeout ;
	int32_t		connecting_elapse ;
	int32_t		data_transmission_timeout ;
	int32_t		data_transmission_elapse ;
} ;

int ZlangWaitforTcp( struct ZlangRuntime *rt , short event , int fd , int32_t *timeout , int32_t *elapse );
int32_t ZlangSendTcp( struct ZlangRuntime *rt , int sock , char *buf , int32_t write_len , int32_t *timeout , int32_t *elapse );
int32_t ZlangReceiveTcp( struct ZlangRuntime *rt , int sock , char *buf , int32_t read_len , int32_t *timeout , int32_t *elapse );

int ZlangListenTcp( struct ZlangRuntime *rt , struct ZlangTcp *tcp , char *ip , int32_t port );
int ZlangAcceptTcp( struct ZlangRuntime *rt , struct ZlangTcp *listen_tcp , struct ZlangTcp *accept_tcp );
int ZlangConnectTcp( struct ZlangRuntime *rt , struct ZlangTcp *tcp , char *ip , int32_t port );
int ZlangCloseTcp( struct ZlangRuntime *rt , struct ZlangTcp *tcp );

#define ZLANG_TCP_GET_IP(_tcp_)								( (_tcp_).ip )
#define ZLANG_TCP_GET_PORT(_tcp_)							( (_tcp_).port )
#define ZLANG_TCP_SET_SOCK(_tcp_,_sock_)						(_tcp_).sock = (_sock_) ;
#define ZLANG_TCP_GET_SOCK(_tcp_)							( (_tcp_).sock )
#define ZLANG_TCP_IS_SOCK_INVALID(_tcp_)						( (_tcp_).sock == INVALID_SOCKET )
#define ZLANG_TCP_IS_SOCK_VALID(_tcp_)							( (_tcp_).sock != INVALID_SOCKET )
#define ZLANG_TCP_SET_CONNECTINGTIMEOUT(_tcp_,_connecting_timeout_)			(_tcp_).connecting_timeout = (_connecting_timeout_) ;
#define ZLANG_TCP_GET_CONNECTINGELAPSE(_tcp_)						(_tcp_).connecting_elapse
#define ZLANG_TCP_SET_DATATRANSMISSIONTIMEOUT(_tcp_,_data_transmission_timeout_)	(_tcp_).data_transmission_timeout = (_data_transmission_timeout_) ;
#define ZLANG_TCP_GET_DATATRANSMISSIONTIMEOUT(_tcp_)					(_tcp_).data_transmission_timeout
#define ZLANG_TCP_SET_DATATRANSMISSIONELAPSE(_tcp_,_data_transmission_elapse_)		(_tcp_).data_transmission_elapse = (_data_transmission_elapse_) ;
#define ZLANG_TCP_GET_DATATRANSMISSIONELAPSE(_tcp_)					(_tcp_).data_transmission_elapse

#define ZLANG_TCP_ERROR				-1
#define ZLANG_TCP_TIMEOUT			-2
#define ZLANG_TCP_DISCONNECTED_FROM_PEER	-3

struct ZlangDirectProperty_htmlsection
{
	struct FasterSection	*sec ;
	unsigned char		no_need_for_destroy ;
} ;

struct FasterSection *GetHtmlSection( struct ZlangDirectProperty_htmlsection *htmlsection );

#define ZLANG_OBJECT_ip			"ip"
#define ZLANG_OBJECT_tcp		"tcp"
#define ZLANG_OBJECT_http		"http"
#define ZLANG_OBJECT_httpclient		"httpclient"
#define ZLANG_OBJECT_httpserver		"httpserver"
#define ZLANG_OBJECT_htmlsection	"htmlsection"

#define EXCEPTION_CODE_SOCKET_ERROR			ZLANG_TCP_ERROR
#define EXCEPTION_CODE_SOCKET_TIMEOUT			ZLANG_TCP_TIMEOUT
#define EXCEPTION_CODE_SOCKET_CLOSED_FROM_PEER		ZLANG_TCP_DISCONNECTED_FROM_PEER
#define EXCEPTION_CODE_HTTP_ERROR			-11
#define EXCEPTION_CODE_HTTPCLIENT_ERROR			-21
#define EXCEPTION_CODE_HTTPSERVER_ERROR			-31
#define EXCEPTION_CODE_HTMLSECTION_ERROR		-81

#define EXCEPTION_MESSAGE_LISTEN_SOCKET_FAILED		"listen socket failed"
#define EXCEPTION_MESSAGE_ACCEPT_SOCKET_FAILED		"accept socket failed"
#define EXCEPTION_MESSAGE_CONNECT_SOCKET_FAILED		"connect socket failed"
#define EXCEPTION_MESSAGE_CLOSE_SOCKET_FAILED		"close socket failed"
#define EXCEPTION_MESSAGE_SOCKET_INVALID		"socket invalid"
#define EXCEPTION_MESSAGE_SOCKET_ERROR			"socket error"
#define EXCEPTION_MESSAGE_SEND_SOCKET_FAILED		"send socket failed"
#define EXCEPTION_MESSAGE_SEND_SOCKET_TIMEOUT		"send socket timeout"
#define EXCEPTION_MESSAGE_RECEIVE_SOCKET_FAILED		"receive socket failed"
#define EXCEPTION_MESSAGE_RECEIVE_SOCKET_TIMEOUT	"receive socket timeout"
#define EXCEPTION_MESSAGE_SOCKET_CLOSED_FROM_PEER	"socket closed from peer"
#define EXCEPTION_MESSAGE_HTTP_METHOD_INVALID		"http method invalid"
#define EXCEPTION_MESSAGE_HTTP_URI_INVALID		"http uri invalid"
#define EXCEPTION_MESSAGE_HTTP_VERSION_INVALID		"http version invalid"
#define EXCEPTION_MESSAGE_CREATE_HTTP_ENVIRONMENT_FAILED	"create http environment failed"
#define EXCEPTION_MESSAGE_NO_HTTP_ENVIRONMENT		"no http environment"
#define EXCEPTION_MESSAGE_REQUESTHTTP_FAILED		"request http failed"
#define EXCEPTION_MESSAGE_GET_HTTP_STATUS_CODE_FAILED	"get http status code failed"
#define EXCEPTION_MESSAGE_QUERY_HTTP_HEADER_FAILED	"query http header failed"
#define EXCEPTION_MESSAGE_GET_HTTP_BODY_FAILED		"get http body failed"
#define EXCEPTION_MESSAGE_RECEIVE_HTTP_REQUEST_FAILED	"receive http request failed"
#define EXCEPTION_MESSAGE_GET_HTTP_METHOD_FAILED	"get http method failed"
#define EXCEPTION_MESSAGE_GET_HTTP_URI_FAILED		"get http uri failed"
#define EXCEPTION_MESSAGE_HTTP_URI_PATH_SEGMENT_NO_INVALID	"http uri segment no invalid"
#define EXCEPTION_MESSAGE_NO_HTTP_URI_PARAMETER		"no http uri parameter"
#define EXCEPTION_MESSAGE_INSTANTIATE_TEMPLATE		"instantiate template"
#define EXCEPTION_MESSAGE_RUN_HTTPSERVER_FAILED		"run httpserver failed"
#define EXCEPTION_MESSAGE_ANALYSE_URL_FAILED		"analyse url failed"
#define EXCEPTION_MESSAGE_GET_IP_BY_DOMAIN_NAME_FAILED	"get ip by domain name failed"
#define EXCEPTION_MESSAGE_ASSEMBLE_HTTP_REQUEST_HEADERS_FAILED	"assemble http request headers failed"
#define EXCEPTION_MESSAGE_ASSEMBLE_HTTP_REQUEST_BODY_FAILED	"assemble http request body failed"
#define EXCEPTION_MESSAGE_SSL_NEW_FAILED		"ssl new failed"
#define EXCEPTION_MESSAGE_SSL_CONNECT_FAILED		"ssl connect failed"
#define EXCEPTION_MESSAGE_EXTRACT_HTTP_RESPONSE_HEADERS_FAILED		"extract http response headers failed"
#define EXCEPTION_MESSAGE_EXTRACT_HTTP_RESPONSE_BODY_FAILED		"extract http response body failed"
#define EXCEPTION_MESSAGE_SET_SHORT_FAILED		"set short failed"
#define EXCEPTION_MESSAGE_SET_INT_FAILED		"set int failed"
#define EXCEPTION_MESSAGE_SET_LONG_FAILED		"set long failed"
#define EXCEPTION_MESSAGE_SET_FLOAT_FAILED		"set float failed"
#define EXCEPTION_MESSAGE_SET_DOUBLE_FAILED		"set double failed"
#define EXCEPTION_MESSAGE_SET_STRING_FAILED		"set string failed"
#define EXCEPTION_MESSAGE_CREATE_SECTION_FAILED		"create section failed"
#define EXCEPTION_MESSAGE_CREATE_DETAIL_FAILED		"create detail failed"
#define EXCEPTION_MESSAGE_NO_HTTP_BODY			"no http body"
#define EXCEPTION_MESSAGE_INET_ATON_FAILED		"inet_aton failed"
#define EXCEPTION_MESSAGE_HTML_SECTION_EMPTY		"html section empty"
#define EXCEPTION_MESSAGE_FTCREATETEMPLATE_FAILED	"FTCreateTemplate failed"
#define EXCEPTION_MESSAGE_FTINSTANTIATETEMPLATE_FAILED	"FTInstantiateTemplate failed"
#define EXCEPTION_MESSAGE_STRDUPENTIREFILE_FAILED	"StrdupEntireFile failed"

DLLEXPORT ZlangImportObjectsFunction ZlangImportObjects;
int ZlangImportObjects( struct ZlangRuntime *rt );

#endif

